package com.xsz.pda.data.model

import android.text.TextUtils
import android.util.Log
import com.alibaba.fastjson.JSON
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.Utils
import com.fasterxml.uuid.Generators
import com.xsz.pda.App
import com.xsz.pda.AppExecutors
import com.xsz.pda.Config
import com.xsz.pda.api.TicketServiceApi
import com.xsz.pda.api.beans.ApiCheckRecord
import com.xsz.pda.data.bean.*
import com.xsz.pda.data.db.LocalDB
import com.xsz.pda.event.ShowDownloadSize
import com.xsz.pda.net.Constant.SYNC_CHECK_RECORD
import com.xsz.pda.utils.DateTimeUtil
import com.xsz.pda.net.udp.UDPHelper
import org.greenrobot.eventbus.EventBus

import java.util.*
import kotlin.collections.ArrayDeque

object TicketModel {
    private val TAG = "TicketModel"
    private val api = TicketServiceApi(Config.ip)

    private val db = LocalDB.get(Utils.getApp())
    private val ticketDao = db.ticketDao()
    private val checkRecordDao = db.checkRecordDao()
    private val ticketItemDao = db.ticketItemDao()
    private val typeGroupDao = db.typeGroupDao()
    //项目ID
    //是否再运行中
    private var running = true
    private var initSize =  100_0000

    private val androidId = DeviceUtils.getAndroidID()

    private val lockSyncRecord = Object()

    private val handledQueue = ArrayDeque<String>(30)

    private const val syncFaceTime = -100L


    private fun notHandled(id: String): Boolean {
        return if (handledQueue.contains(id)) {
            Log.d(TAG, "过滤重复消息：$id")
            true
        } else {
            Log.d(TAG, "处理新消息：$id")
            while (handledQueue.size >= 30) {
                handledQueue.removeFirst()
            }
            handledQueue.add(id)
            false
        }
    }
    private var initDataThread:Thread? = null
    private var syncTicketThread:Thread? = null
    private var uploadThread:Thread? = null
    fun start() {
        running = true
        /**
         * 同步新票证线程
         */
        initDataThread = Thread({
            while (running) {

                val projectSize = Config.getProjectTicketSize()
                val ticketSize = ticketDao.getTicketSize() ?: 0

                if (projectSize != 0) {
                    initSize = projectSize
                    EventBus.getDefault().post(ShowDownloadSize(ticketDao.getTicketSize()?:0, initSize))
                }

                if (ticketSize < initSize) {
                    api.initData(ticketDao.getLastId()?: "-1", Config.getProjectId(), androidId)?.let {
                        initSize = it.ticketSize
                        val ticketList = arrayListOf<Ticket>()
                        it.ticketList?.forEach { at ->
                            ticketList.add(
                                Ticket(
                                    at.id,
                                    at.qrcode,
                                    at.m1,
                                    at.idNumber,
                                    at.ticketTypeId,
                                    at.delFlag,
                                    it.timestamp,
                                    at.signStatus,
                                    at.m1Value,
                                    at.name,
                                    at.status ?: "1",
                                    seat = at.seat ?: "",
                                    areaName = at.areaName ?: "",
                                    sectorName = at.sectorName ?: "",
                                    outerId = at.outerId ?: ""
                                )
                            )
                        }
                        val itemList = arrayListOf<TicketItem>()
                        it.ticketItemList?.forEach { item->
                            itemList.add(TicketItem(
                                item.id,
                                item.startTime,
                                item.endTime,
                                it.timestamp,
                                item.ticketId,
                                item.canuseTimes,
                                item.chuCanuseTimes))
                        }
                        itemList.forEach {item->
                            val old = ticketItemDao.getById(item.id)
                            if(old == null){
                                ticketItemDao.inertAll(listOf(item))
                            }
                        }
                        ticketList.forEach{item->
                            val old = ticketDao.findById(item.id)
                            if(old == null){
                                ticketDao.insertAll(listOf(item))
                            }
                        }
//                   ticketItemDao.inertAll(itemList)
//                   ticketDao.insertAll(ticketList)
                        val downloadSize = ticketDao.getTicketSize() ?: 0
                        EventBus.getDefault().post(ShowDownloadSize(ticketDao.getTicketSize()?:0, initSize))
                    }
                    try { Thread.sleep(500) } catch (_: InterruptedException){ }
                }
                //没有更新任务时，同步线程来唤醒
                if (initSize == 0 || ticketSize == initSize){
                    try { Thread.sleep(2 * 60 * 1000) } catch (_: InterruptedException){ }
                }
            }
        },"initDataThread")

        //更新票证
        syncTicketThread = Thread ({
            try {
                Thread.sleep(3000)
            } catch (_: InterruptedException) { }
            while (running) {
                api.syncData(
                    ticketDao.getLastSyncTime() ?: 0,
                    0,
                    ticketItemDao.getLastSyncTime() ?: 0,
                    Config.getProjectId(),
                    ticketDao.getLastId() ?: "-1",
                    androidId,
                    syncFaceTime,
                    if(App.openFirstCheck) "Y" else "N"
                )?.let { st ->
                    EventBus.getDefault().post(ShowDownloadSize(ticketDao.getTicketSize()?:0, st.ticketSize))
                    //更新数据时发现该项目新添加了数据时，唤醒初始数据线程去拉新数据
                    if (initSize != st.ticketSize) {
                        initSize = st.ticketSize
                        Config.setProjectTicketSize(st.ticketSize)
                        initDataThread?.interrupt()
                    }
                    if (st.ticketList != null && st.ticketList.isNotEmpty()) {
                        st.ticketList.forEach { at ->
                            //有必要时更新组信息
                            val old = ticketDao.findById(at.id)
                            if (old != null) {
                                val ticket = Ticket(
                                    id = at.id,
                                    qrcode = at.qrcode,
                                    m1 = at.m1,
                                    idNumber = at.idNumber,
                                    ticketTypeId = at.ticketTypeId,
                                    delFlag = at.delFlag,
                                    timestamp = st.timestamp,
                                    signStatus = at.signStatus,
                                    m1Value = at.m1Value,
                                    name = at.name,
                                    status = at.status ?: "1",
                                    seat = at.seat ?: "",
                                    areaName = at.areaName ?: "",
                                    sectorName = at.sectorName ?: "",
                                    outerId = at.outerId ?: ""
                                )
                                ticketDao.update(ticket)
                            }
                        }
                    }
                    if (st.ticketItemList != null && st.ticketItemList.isNotEmpty()) {
                        val list =st.ticketItemList.map {
                            TicketItem(
                                it.id,
                                it.startTime,
                                it.endTime,
                                st.timestamp,
                                it.ticketId,
                                it.canuseTimes,
                                it.chuCanuseTimes
                            )
                        }
                        ticketItemDao.update(list)
                    }
                }
                try {
                    Thread.sleep(1000)
                } catch (_: InterruptedException) {  }
            }
        },"syncCheckThread")
        uploadThread = Thread({
            while (running) {
                try { Thread.sleep(3 * 1000) } catch (_: InterruptedException){ }
                val records = checkRecordDao.findNotSynced()
                if (records.isNotEmpty()) {
                    val apiCheckRecords = arrayListOf<ApiCheckRecord>()
                    records.forEach {
                        val acr =
                            ApiCheckRecord(it.ticketItemId, it.checkTime, androidId, it.id, androidId)
                        acr.type = it.type
                        acr.firstCheck = if(App.openFirstCheck) "Y" else "N"
                        apiCheckRecords.add(acr)
                    }
                    if (api.syncData(apiCheckRecords)){
                        records.forEach { it.status = 1 }
                        checkRecordDao.updateRecords(records)
                        Log.d(TAG,"验票记录同步成功")
                    }
                    else {  Log.d(TAG,"验票记录同步失败") }
                }
            }
        },"uploadThread")

        initDataThread?.start()
        uploadThread?.start()
        syncTicketThread?.start()
        udpStart()
    }


    private fun udpStart() {
        udp = UDPHelper()
        udp?.setMsgListener(SYNC_CHECK_RECORD){ json, _-> AppExecutors.diskIO.execute { doSyncCheckRecord(json) } }
        udp?.start()
    }


    private fun doSyncCheckRecord(itemJson: String) {
        val item = JSON.parseObject(itemJson, ApiCheckRecord::class.java)
        if (notHandled(item.id)) {
            if (androidId != item.androidId) {
                synchronized(lockSyncRecord){
                    if (checkRecordDao.queryById(item.id) == null) {
                        ticketItemDao.getById(item.ticketItemId ?:"")?.let {
                            // 是初检就减少初检的验票次数
                            if (App.openFirstCheck) {
                                it.chuCanuseTimes = it.chuCanuseTimes - 1
                            }
                            // 不是初检就减少闸机的验票次数
                            else{
                                it.canuseTimes = it.canuseTimes - 1
                            }
                            ticketItemDao.update(it)
                        }
                        val record = CheckRecord(
                            item.id,
                            item.ticketItemId,
                            item.checkTime,
                            item.type,
                            2,
                            item.androidId,
                            item.deviceName,
                            item.groupId,
                            0,
                            if (App.openFirstCheck) "Y" else "N"
                        )
                        checkRecordDao.insert(record)
                    }
                }
            }
        }
       udpStart()
    }
    var udp : UDPHelper?= null

    fun stop(){
        running = false
        initDataThread?.interrupt()
        uploadThread?.interrupt()
        syncTicketThread?.interrupt()
        udp?.stop()
    }

    fun check(qrcode: String?, m1: String?,idNumber:String?): Triple<ValidateTicket?, String, Int> {
        val ticket = if (qrcode != null) {
            ticketDao.findByQrcode(qrcode)
        } else if (m1 != null) {
            ticketDao.findByM1(m1)
        } else {
            ticketDao.findByIdNumber(idNumber!!.replace('x', 'X').trim().trim())
        }
        if (ticket != null) {
            val t = ticket.ticket
            var seat = ""
            if (!TextUtils.isEmpty(t.areaName)) {
                seat += t.areaName+ " "
            }
            if (!TextUtils.isEmpty(t.sectorName)) {
                seat += t.sectorName + " "
            }
            if (!TextUtils.isEmpty(t.seat)) {
                seat += t.seat
            }
            val deviceGroupOk = Config.getTicketTypeIds().contains(t.ticketTypeId)
            if (deviceGroupOk) {
                val now = Date()
                var findDateSuccess = false
                for (item in ticket.items) {
                    var startTimePass = true
                    item.startTime?.let {
                        val startTime = DateTimeUtil.parseDate(it, DateTimeUtil.TIME_PATTERN)
                        startTimePass = DateTimeUtil.compareDate(now, startTime) >= 0
                    }
                    var endTimePass = true
                    item.endTime?.let {
                        val startTime = DateTimeUtil.parseDate(it, DateTimeUtil.TIME_PATTERN)
                        endTimePass = DateTimeUtil.compareDate(now, startTime) <= 0
                    }
                    if (startTimePass && endTimePass) {
                        findDateSuccess = true
                        // 根据是否为初检判断用初检的次数还是过闸的次数
                        val times = if (App.openFirstCheck) {
                            item.chuCanuseTimes
                        } else item.canuseTimes
                        if (times > 0) {
                            val lastCheck = checkRecordDao.getLastCheck(ticket.items.map { it.id })
                            var groupName = ""
                            var lastInTime = ""
                            if (lastCheck != null) {
                                lastInTime = lastCheck.checkTime
                                typeGroupDao.queryByGroupId(lastCheck.groupId)?.let { groupName = it.name }
                            }
                            saveRecord(item)
                            return Triple(ValidateTicket(groupName, lastInTime, seat), "验证成功", 0)
                        }
                    }
                }
                return if (findDateSuccess) {
                    Triple(null, "无使用次数", 4)
                } else {
                    Triple(null, "入场时间错误", 3)
                }
            } else {
                return Triple(null, "$seat,请走${t.areaName}", 2)
            }
        }
        return Triple(null, "票证不存在",1)
    }

    /**
     * 保存验证成功的记录
     */
    private fun saveRecord(ticketItem: TicketItem, nowSync:Boolean = true) {
        AppExecutors.diskIO.execute {
            val fistCheck = if (App.openFirstCheck) "Y" else "N"
            val checkTime = DateTimeUtil.format(Date(), DateTimeUtil.TIME_PATTERN)
            val id = Generators.timeBasedGenerator().generate().toString().replace("-", "")
            val item = CheckRecord(
                id,
                ticketItem.id,
                checkTime,
                CheckRecord.TYPE_NO_FACE,
                0,
                androidId,
                androidId,
                Config.getGroupId(),
                0,
                fistCheck
            )
            checkRecordDao.insert(item)
            //减少一次可使用次数
            if (App.openFirstCheck) {
                ticketItem.chuCanuseTimes = ticketItem.chuCanuseTimes - 1
            }else{
                ticketItem.canuseTimes = ticketItem.canuseTimes - 1
            }

            ticketItemDao.update(ticketItem)
            if (nowSync){
                //保存图片
                val record = ApiCheckRecord()
                record.id = id
                record.groupId = Config.getGroupId()
                record.androidId = androidId
                record.deviceName = androidId
                record.checkTime = checkTime
                record.ticketItemId = ticketItem.id
                record.type = CheckRecord.TYPE_NO_FACE
                record.firstCheck = fistCheck
                //局域网同步
                udp?.syncRecord(record)
                //服务器同步
                uploadThread?.interrupt()
            }
        }
    }

    fun clearProjectData() {
        checkRecordDao.clear()
        ticketDao.clear()
        ticketItemDao.clear()
        Config.setProjectTicketSize(0)
    }

    fun findTicketByIdNumber(idNumber: String) = ticketDao.likeFindByIdNumber(idNumber)


}